/*
 * Copyright:  2018 smarabbit studio.
 *
 *  Licensed under the Confluent Community License; you may not use this file
 *  except in compliance with the License.  You may obtain a copy of the License at
 *
 *  http://www.confluent.io/confluent-community-license
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 *  @作   者： 黄开晖 (117227773@qq.com)
 *  @日   期:  2019年1月7日
 */

package com.massyframework.beanskin.spring.beancontext;


import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.GenericBeanDefinition;

import com.massyframework.beanskin.ModuleListener;
import com.massyframework.beanskin.assembly.Assembly;
import com.massyframework.beanskin.assembly.AssemblyConfig;
import com.massyframework.beanskin.assembly.AssemblyContext;
import com.massyframework.beanskin.assembly.AssemblyContextAware;
import com.massyframework.beanskin.assembly.handling.DependencyServiceProvider;
import com.massyframework.beanskin.assembly.handling.HandlerBase;
import com.massyframework.beanskin.assembly.handling.HandlerRegistration;
import com.massyframework.beanskin.service.DependencyServiceDescriptor;
import com.massyframework.beanskin.service.ServiceRepository;


/**
 *  {@link DependencyServiceBeanDefinitionRegistryPostProcessor} 依赖服务Bean定义注册处理器
 */
public class DependencyServiceBeanDefinitionRegistryPostProcessor  extends HandlerBase
        implements BeanDefinitionRegistryPostProcessor, AssemblyContextAware {

    private volatile Map<String, Object> dependenciesMap;
    private BeanLifecycleProcessor processor;
    private HandlerRegistration<?> registration;

    /**
     * 构造方法
     */
    public DependencyServiceBeanDefinitionRegistryPostProcessor() {
    	this.processor = new BeanLifecycleProcessor();
    }
    
    @Override
	protected void init() throws Exception {
		super.init();
		if (this.registration == null) {
			String text = this.getAssociatedAssembly().getConfig().getParameter("lazy.init", "true");
	        Boolean defaultLazyInit = text == null ? null : Boolean.parseBoolean(text);
	        SimpleLazyInitHandler handler = new SimpleLazyInitHandler(defaultLazyInit);
	        this.registration = this.getLifecycleManager().register(handler);
		}
	}

	@Override
	public void destroy() {
		if (this.registration != null) {
			this.registration.unregister();
			this.registration = null;
		}
		super.destroy();
	}

	/**
     * 向BeanDefinitionRegistry注册Bean
     * @param beanDefinitionRegistry {@link BeanDefinitionRegistry}, bean定义注册器
     * @throws BeansException 注册时发生非预期例外
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        if (this.dependenciesMap == null) {
            this.dependenciesMap = new HashMap<String, Object>();
            Assembly assembly = this.getAssociatedAssembly();
            ServiceRepository serviceRepository = ServiceRepository.retrieveFrom(assembly);

            //注入装配件
            this.injectBeanDefinition("assembly", Assembly.class,
                    assembly, beanDefinitionRegistry);
            this.injectBeanDefinition("assemblyConfig",AssemblyConfig.class,
            		assembly.getConfig(), beanDefinitionRegistry);
            this.injectBeanDefinition("serviceRepository", ServiceRepository.class,
                    serviceRepository, beanDefinitionRegistry);
            
            this.dependenciesMap.put("assembly", assembly);
            this.dependenciesMap.put("assemblyConfig", assembly.getConfig());
            this.dependenciesMap.put("serviceRepository", serviceRepository);

            Map<DependencyServiceDescriptor<?>, Object> serviceMap = this.getDependencyServices();
            if (serviceMap != null) {
                for (Map.Entry<DependencyServiceDescriptor<?>, Object> entry : serviceMap.entrySet()) {
                	DependencyServiceDescriptor<?> descriptor = entry.getKey();
                    Object service = entry.getValue();


                    //注入Bean
                    this.injectBeanDefinition(
                    		descriptor.getBeanName(), descriptor.getRequiredType(), service, beanDefinitionRegistry);
                    this.dependenciesMap.put(descriptor.getBeanName(), service);
                }
            }
        }
    }

    /**
     * BeanFactory 后置处理
     * @param configurableListableBeanFactory {@link ConfigurableListableBeanFactory}, 可配置的SpringBean工厂
     * @throws BeansException  注册时发生非预期例外
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
    	configurableListableBeanFactory.addBeanPostProcessor(this.processor);
    }
    
    /**
     * 注入Bean定义
     * @param beanName bean名称，可以为null.
     * @param beanType  bean类型
     * @param bean bean实例
     * @param beanDefinitionRegistry bean定义注册器
     */
    protected void injectBeanDefinition(String beanName, Class<?> beanType,
                                        Object bean, BeanDefinitionRegistry beanDefinitionRegistry){
        GenericBeanDefinition definition = new GenericBeanDefinition();
        definition.setLazyInit(true);

        definition.setBeanClass(DependencyServiceFactoryBean.class);
        MutablePropertyValues propertyValue = definition
                .getPropertyValues();
        propertyValue.add("objectType", beanType);
        propertyValue.add("object", bean);

        //使用组件编号注册Bean定义
        if (beanName == null){
            beanName = beanType.getName();
        }
        beanDefinitionRegistry.registerBeanDefinition(beanName, definition);
    }

    /**
     * 获取依赖服务实例
     * @return {@link Map}, 可能返回null.
     */
    protected Map<DependencyServiceDescriptor<?>, Object> getDependencyServices(){
        DependencyServiceProvider handler =
                this.findHandler(DependencyServiceProvider.class);
        if (handler != null) {
            return handler.getDependencyServices();
        }
        return Collections.emptyMap();
    }
    
    

    @Override
	public void setAssemblyContext(AssemblyContext context) {
		this.processor.bindAssemblyContext(context);
	}

    /**
     * {@link BeanLifecycleProcessor} bean生命周期处理器
     */
    private class BeanLifecycleProcessor implements BeanPostProcessor, DestructionAwareBeanPostProcessor {

        private AssemblyContext context;

        public BeanLifecycleProcessor() {
        }

        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            if (this.isInjectBean(beanName)) return bean;
            this.bindBean(bean);
            return bean;
        }

        @Override
        public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException{
        	if (this.isInjectBean(beanName)) return;
        	
        	Assembly assembly = getAssociatedAssembly();
            this.unbindBean(bean, assembly);
        }
        
        @Override
		public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
			return bean;
		}
        
        @Override
		public boolean requiresDestruction(Object bean) {
			return false;
		}

        /**
         * 绑定{@link AssemblyContext}
         */
        protected void bindAssemblyContext(AssemblyContext assemblyContext){
            this.context = assemblyContext;
        }

        /**
         * 绑定bean
         * @param bean {@link Object},bean实例
         */
        private void bindBean(Object bean){
        	Assembly assembly = getAssociatedAssembly();
            this.bindBean(bean, assembly, this.context);
        }

        /**
         * 绑定bean
         * @param bean {@link Object}, bean实例
         * @param assembly {@link Assembly}, 装配件
         * @param context {@link AssemblyContext}, 装配件上下文
         */
        private void bindBean(Object bean, Assembly assembly, AssemblyContext context){
            if (bean instanceof ModuleListener){
                assembly.addListener((ModuleListener)bean);
            }
            AssemblyContextAware.tryToBind(bean, context);
        }
        /**
         * 取消bean绑定
         * @param bean {@link Object}, bean实例
         * @param assembly {@link Assembly}, 装配件
         */
        private void unbindBean(Object bean, Assembly assembly){
            AssemblyContextAware.tryToBind(bean, null);
            if (bean instanceof ModuleListener){
                assembly.removeListener((ModuleListener)bean);
            }
        }

        /**
         * 判断是否为注入的依赖服务
         * @param beanName {@link String}, bean名称
         * @return  {@link boolean}, 返回<code>true</code>表示是
         */
        private boolean isInjectBean(String beanName){
            if (beanName == null) return false;
            return dependenciesMap.containsKey(beanName);
        }
    }
}
